package cn.wonderland.basic.aop;

import cn.wonderland.basic.anno.LogAnnotation;
import cn.wonderland.basic.util.HttpContextUtils;
import cn.wonderland.basic.util.IpUtil;
import cn.wonderland.log.domain.Log;
import cn.wonderland.log.service.LogService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;

@Component
@Aspect
@Slf4j
public class LogAspect {

    @Autowired
    private LogService logService;

    @Pointcut("@annotation(cn.wonderland.basic.anno.LogAnnotation)")
    public void pointcut() {
    }

    /*
    * 代码中的@Around注解表示这是一个环绕通知；
    * pointcut()方法定义了我们要切入的目标方法，
    * 也就是通过这个方法来确定到底对哪个方法进行通知；
    * log()方法是具体的通知逻辑，当目标方法被调用时，
    * 会先记录调用开始时间，然后执行目标方法，最后记录调用结束时间，并计算出方法执行时间。
    * 最后，我们将记录的信息保存起来，然后返回目标方法的执行结果。
     * */
    @Around("pointcut()")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long beginTime = System.currentTimeMillis();//执行方法
        Object result = joinPoint.proceed();//执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;//保存日志
        recordLog(joinPoint, time);
        return result;
    }

    private void recordLog(ProceedingJoinPoint joinPoint, long time) {

        Log log1 = new Log();

        /*
         * 1. 首先，通过joinPoint.getSignature()方法获取到连接点对象的签名信息，
         * 然后强制转换为 MethodSignature 类型的对象 signature ，
         * 这个对象中包含了方法的签名信息(包括所属类的名称、方法名称、参数类型等)。  。
         * */
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        /*
         * 2.然后，通过 signature.getMethod() 方法获得目标方法对象 method ，即被拦截的方法对象。
         * 获取到该方法对象之后，可以通过getAnnotation()方法获取该方法上的注解信息。
         * */
        Method method = signature.getMethod();
        /*
         * 3.在这里，通过method.getAnnotation(LogAnnotation.class)方法获取了被拦截的方法上的自定义注解
         * LogAnnotation 的信息，并将其赋值给了变量 logAnnotation 。
         * */
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        if (logAnnotation != null){
            String operator = logAnnotation.operator();
            String module = logAnnotation.module();
            log1.setModule(module);
            log1.setOperation(operator);
        }


        log.info("=========log start===========-");
        // 输出相关的模块信息和操作员信息
        log.info("module:{}", logAnnotation.module());

        log.info("operation:{}", logAnnotation.operator());
        // 获取请求的方法名和请求参数，并输出
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log1.setMethod(className + "." + methodName);

        log.info("request method:{}", className + "," + methodName + "()");

        //请求的参数
        Object[] args = joinPoint.getArgs();
        String params = JSON.toJSONString(args[0]);
        log1.setParams(params);
        log1.setCreateDate(new Date());

        log.info("params:{}", params);
        // 获取request  获取请求者的IP地址，并输出
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log1.setIp(IpUtil.getIpAddress(request));

        log.info("ip:{}", IpUtil.getIpAddress(request));
        log.info("excute time :{} ms", time) ;
        log.info("==========log end========");
        //调用service保存Log实体类到数据库
        logService.add(log1);
    }
}
